home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d2 / njramd12.arc / NJRAMD.ASM < prev    next >
Assembly Source File  |  1988-02-26  |  42KB  |  1,432 lines

  1.  
  2. ;  Nifty James' Famous Expanded Memory Disk Drive
  3. ;  (C) Copyright 1987 by Mike Blaszczak.  All Rights Reserved
  4.  
  5. ;  Version 1.01  of  24 May 1987
  6. ;  Version 1.10  of  25 May 1987
  7. ;  Version 1.15  of  31 May 1987
  8. ;  Version 1.20  of  16 Oct 1987
  9.  
  10. ;  Shareware  $10     Please contribute!
  11.  
  12. ;  Assemble with
  13. ;    MASM NJRAMD;
  14. ;        (Use MASM /DV286 NJRAMD; to assemble 286 version.)
  15. ;    LINK NJRAMD;
  16. ;    EXE2BIN NJRAMD.EXE NJRAMD.SYS
  17. ;    DEL NJRAMD.EXE
  18.  
  19. ;  --> DEVICE DRIVER FORMAT FILE <--
  20. ;  -->  REMEMBER TO USE EXE2BIN  <--
  21.  
  22. ; ---------------------------------------------------------------------------
  23.  
  24. ;   ASCII Characters
  25.  
  26. bell        equ    7        ; bell character
  27. tab        equ    9        ; tab character
  28. lf        equ    10        ; linefeed
  29. cr        equ    13        ; carriage return
  30. space        equ    32        ; space
  31. eos        equ    '$'        ; end of DOS string
  32.  
  33. ; ---------------------------------------------------------------------------
  34.  
  35. EMM        equ    067h        ; the E/EMS memory manager
  36.  
  37. ; ---------------------------------------------------------------------------
  38. ;   I/O Ports
  39.  
  40. Speak        equ    061h        ; speaker port
  41. SpeakMask    equ    011111110b    ; mask for speaker set bit
  42. SpeakToggle    equ    000000010b    ; toggle bit for the speaker
  43.  
  44. ; ---------------------------------------------------------------------------
  45. ;   DOS Calls
  46.  
  47. ; These are DOS functions used by the driver.
  48.  
  49. DisplayOut    equ    002h        ; call to print a single character
  50. PrintString    equ    009h        ; call to print a '$' string
  51. GetDOSVersion    equ    030h        ; call to get the DOS version #
  52.  
  53. ; ---------------------------------------------------------------------------
  54. ;  E/EMM Routines
  55.  
  56. ; These are the E/EMM functions that we use.  (These are specific functions
  57. ; of the EMM interrupt.)
  58.  
  59. E_PageBase    equ    041h        ; determine the Page Fram Base Addr
  60. E_Counts    equ    042h        ; determine free/total mem
  61. E_Open        equ    043h        ; open, allocate, obtain handle ID
  62. E_MapPage    equ    044h        ; map a logical page into window
  63. E_Version    equ    046h        ; get the E/EMM version number
  64. E_Save        equ    047h        ; save mapping context
  65. E_Restore    equ    048h        ; restore mapping context
  66.  
  67. ; ---------------------------------------------------------------------------
  68. ;  Driver Equates
  69.  
  70. ; This is the media descriptor byte.  Since our RAM drive is not 2 sided,
  71. ; does not have 8 sectors per track, and is not removable, we use 0F8h.
  72. ; At least, that's what the IBM DTR manual says.
  73.  
  74. MediaD        equ    0F8h
  75.  
  76. ; These are equates used by the driver.  They are all status and
  77. ; error flags, as defined in the DOS Technical Reference Manual.
  78.  
  79. ;                        FEDCBA9876543210 <- BIT NUMBERS
  80. errorflag    equ    01000000000000000b    ; error bit flag
  81. busystat    equ    00000001000000000b    ; busy status bit flag
  82. donestat    equ    00000000100000000b    ; done status bit flag
  83.  
  84. err_writeprot    equ    0        ; write protect violation
  85. err_badunit    equ    1        ; unknown unit number
  86. err_notready    equ    2        ; device not ready
  87. err_unknown    equ    3        ; unknown command
  88. err_CRC        equ    4        ; error CRC command
  89. err_reqlen    equ    5        ; bad request length
  90. err_seek    equ    6        ; seek failure
  91. err_badmedia    equ    7        ; bad media
  92. err_badsector    equ    8        ; sector not found
  93. err_badwrite    equ    10        ; write fault
  94. err_badread    equ    11        ; read fault
  95. err_general    equ    12        ; general failure
  96.  
  97. ; ---------------------------------------------------------------------------
  98. ;  Structure Definitions
  99.  
  100. ;  The structures defined here are used to find information in the
  101. ;  various request header formats.  Of course, being structures, they
  102. ;  don't take up space... they are used to define offsets for the
  103. ;  addressing of the request header.
  104.  
  105. rq    equ    es:[bx]            ; base address used in routines
  106.  
  107. ;  -- Request Header (General Format)
  108.  
  109. rhead    struc
  110.     rlen    db    ?    ; length of the structure
  111.     unitn    db    ?    ; unit number
  112.     command    db    ?    ; command code
  113.     status    dw    ?    ; status code (returned by us)
  114.         db    8 dup(?); reserved bytes
  115. rhead    ends
  116.  
  117.  
  118. ;  -- Request Header (INIT Command)
  119.  
  120. inithead    struc
  121.         db    (type rhead) dup (?)
  122.     units    db    ?    ; number of units
  123.     ndadro    dw    ?    ; ending address offset
  124.     ndadrs    dw    ?    ; ending address segment
  125.     bpboff    dw    ?    ; BPB offset pointer
  126.     bpbseg    dw    ?    ; BPB segment pointer
  127.     taglet    db    ?    ; drive tag letter
  128. inithead    ends
  129.  
  130. ;  -- Request Header (Media Check)
  131.  
  132. mediahead    struc
  133.         db    (type rhead) dup (?)
  134.     media    db    ?    ; our meida descriptor byte
  135.     change    db    ?    ; changed media flag
  136. mediahead    ends
  137.  
  138. ;  -- Request Header (Build BPB)
  139.  
  140. bbpbhead    struc
  141.         db    (type rhead) dup (?)
  142.         db    ?    ; media descriptor byte
  143.     baoff    dw    ?    ; transferr buffer address offset
  144.     baseg    dw    ?    ; transferr buffer address segment
  145.         dw    ?    ; BIOS parameter block pointer
  146.         dw    ?    ; BIOS parameter block pointer
  147. bbpbhead    ends
  148.  
  149. ;  -- Request Header (Read and Write)
  150.  
  151. rwhead        struc
  152.         db    (type rhead) dup (?)
  153.         db    ?    ; media descriptor byte
  154.     tbaoff    dw    ?    ; transferr buffer address offset
  155.     tbaseg    dw    ?    ; transferr buffer address segment
  156.     count    dw    ?    ; sector count
  157.     strtsec    dw    ?    ; starting sector number
  158. rwhead        ends
  159.  
  160.  
  161. ; With these headers defined as they are, access to the request header
  162. ; and command info fields is greatly simplified.  By setting ES:BX to
  163. ; point to the request header, the information can be easily referenced
  164. ; by using constructs such as
  165.  
  166. ;        mov    rq.count,ax
  167. ;  or
  168. ;        mov    al,rq.command
  169.  
  170. ; Note that any part of the program can easily reference any particular
  171. ; command's structure, since the line
  172.  
  173. ;        db    (type rhead) dup (?)
  174.  
  175. ; makes all the command-specific structures "equivalent".
  176.  
  177. ; Check to see if this is the 286 version
  178.  
  179. ifdef  V286
  180.     .286
  181.     if1
  182.         %OUT    Enhanced processor version
  183.     endif
  184.     ifdef    PCL
  185.     if1
  186.         %OUT    for the PC's Limited 286/386
  187.     endif
  188.     endif
  189. else
  190.     if1
  191.         %OUT    Standard Version
  192.     endif
  193. endif
  194.  
  195.  
  196. ;  This macro is used during debugging.  It prints a single character
  197. ; via the BIOS screen interface, and leaves the registers unchanged.
  198.  
  199. ifdef    DEBUG
  200.  
  201.     if1
  202.         %OUT  DEBUG Version
  203.     endif
  204.     PrintChar    macro    Char
  205.         ifdef    PCL
  206.  
  207.             push    ax
  208.             mov    al,Char
  209.             out    095h,al        ; put it digit 3 of smartvu
  210.             pop    ax
  211.  
  212.         else
  213.     
  214.             push    ax        ; save the regs
  215.             push    bx
  216.             push    dx
  217.             mov    ah,15
  218.             int    010h        ; get the current page
  219.             mov    al,Char
  220.             mov    ah,14        ; print the character
  221.             int    010h
  222.  
  223.             xor    dx,dx
  224.             mov    ah,0        ; also to printer
  225.             mov    al,Char
  226.             int    017h
  227.  
  228.             pop    dx
  229.             pop    bx        ;restore the regs
  230.             pop    ax
  231.  
  232.             endif
  233.             endm
  234.  
  235. else
  236.     PrintChar    macro    Char        ; if not debugging, blow it off
  237.             endm
  238. endif
  239.  
  240. ; ---------------------------------------------------------------------------
  241. ;  Public declarations for SYMDEB
  242.  
  243. ; These are public declarations included to allow SYMDEB to know where
  244. ; various lables and addresses are.  They are only needed for debugging,
  245. ; and serve no other useful purpose.
  246.  
  247. PUBLIC NextPlace
  248. PUBLIC Attrib, JumpTable, TopCommand, RBPoint, RBPointOff, RBPointSeg, SaveSS
  249. PUBLIC SaveSP, SaveAX, EMMHandle, EMMBase, StackTop, STRATPROC, Strategy
  250. PUBLIC INTPROC, Interrupt, NoSaveM, FreakOut, IOCTLInput, ReadNoWait
  251. PUBLIC InputStatus, InputFlush, badcommand, BigLog, NoRestore, MC, MediaCheck
  252. PUBLIC BBPB, BuildBPB, BPBArray, OurBoot, OurBPB, SecSize, SecPerCluster
  253. PUBLIC RDirLen, DiskSize, SecPerFAT, BootCode, TAddr, TAddrOff, TAddrSeg
  254. PUBLIC TDone, TCount, TSector, RSEC, Read, ReadLoop, ReadDone
  255. PUBLIC ReadFinish, ReadError, WSEC, Write, WriteLoop, WriteDone, WriteFinish
  256. PUBLIC WriteError, CLIPPER, RangeError, InRange, SPEAKERCLICK, MakeClick
  257. PUBLIC NoClick, SpeakerFlag, LastTime, GS, GetSector, CantGet, LastResident,
  258. PUBLIC EMMPresent, GenFailHook, EMMPresent2, MemForMe, EatingWhite, GotOption
  259. PUBLIC NoBump, NotSilence, PagesLoop, LastDigit, NotPages, NotUseAll,
  260. PUBLIC Unrecognized, EndOfLine, GoodSize, GotPages, BigBust, ReTry
  261. PUBLIC GoodCombo, NoKludge, WipeOut, WipeOut2, FindFree, CalcEMMFree
  262. PUBLIC CalcDiskFree, ClickOkay, MsgOkay, InitFail, GenFail, HowMuch
  263. PUBLIC RqdPages, MajorVersion, OurVolume, Banner, EMMIDString, General
  264. PUBLIC NoEMMThere, EMMError, Init, NoMem, TooBig, BadOption, NoClicking
  265. PUBLIC Installed, DriveName, InstalledB, Installed2, UsedSpace, Bin2Dec
  266. PUBLIC Bin2DecLoop, Bin2DecDigit, WorkAreaL, WorkAreaH
  267.  
  268. ; ---------------------------------------------------------------------------
  269.  
  270. driver        segment    para public 
  271.         assume    cs:driver,ds:driver,es:driver,ss:driver
  272.  
  273.         org    0        ; drivers begin at zero
  274. firstplace    equ    this byte    ; this is the first byte
  275.  
  276. ; ---------------------------------------------------------------------------
  277. ;  Device Header
  278.  
  279. ; This area contains the header information.  It is used by DOS when loading
  280. ; the device driver, and it contains information used to describe the
  281. ; driver to the DOS environment.
  282.  
  283. NextPlace    dw    -1,-1        ; pointer to next driver
  284. Attrib        dw    00010000000000000b        ; attribute word
  285.             ;FEDCBA9876543210
  286.  
  287.                     ; device is non-ibm and block mode
  288.                     ; doesn't support IOCTL, is not
  289.                     ; a network device
  290.  
  291.         dw    offset Strategy        ; the strategy entry
  292.         dw    offset Interrupt    ; the interrupt entry
  293.         db    1,'NJ_DISK'        ;  Nifty James' Disk!
  294.  
  295. ; ---------------------------------------------------------------------------
  296.  
  297. JumpTable    label word
  298.  
  299. ; This area is a "Jump Table" that is used to dispatch the code.
  300. ; Only the functions marked with a "*" in their comment field
  301. ; are actually implemented.  (Since this is a block device, only
  302. ; some of the areas are actually used.)
  303.  
  304.     dw    offset    Init        ;  0 * initialize
  305.     dw    offset    MediaCheck    ;  1 * media check
  306.     dw    offset    BuildBPB    ;  2 * build BIOS parameter block
  307.     dw    offset    IOCTLInput    ;  3   I/O Control (Input)
  308.     dw    offset    Read        ;  4 * read from device
  309.     dw    offset    ReadNoWait    ;  5   read from device (nondest,
  310.                     ;    no wait, char only)
  311.     dw    offset    InputStatus    ;  6   input status
  312.     dw    offset    InputFlush    ;  7   flush pending input
  313.     dw    offset    Write        ;  8 * Write data
  314.     dw    offset    Write        ;  9 * Write data with Verify
  315. ;
  316. ;    dw    offset    OutputStat    ; 10   Output status
  317. ;    dw    offset    OutputFlush    ; 11   flush pending output
  318. ;    dw    offset    IOCTLOutput    ; 12   I/O Control (Output)
  319. ;    dw    offset    DeviceOpen    ; 13   Open Device
  320. ;    dw    offset    DeviceClose    ; 14   Close Device
  321. ;    dw    offset    Removeable    ; 15   Removable media check
  322. ;
  323.     ;  (The commands above 9 are all not implemented -- we don't
  324.     ;   make entries for them to optimize for space (and speed).
  325.     ;   The equate TopCommand must be set to the last used
  326.     ;   command code.)
  327.  
  328. TopCommand    equ    9        ; highest valid command
  329.  
  330. RBPoint        label    dword        ; Pointer to request buffer
  331. RBPointOff    dw    0        ; offset part
  332. RBPointSeg    dw    0        ; segment part
  333.  
  334. SaveSS        dw    0        ; save place for the SS register
  335. SaveSP        dw    0        ; save place for the SP register
  336. SaveAX        dw    0        ; save place for the accumulator
  337.  
  338. EMMHandle    dw    0        ; our handle, as assigned by the EMM
  339. EMMBase        dw    0        ; base of the EMM physical window
  340.  
  341. ; ---------------------------------------------------------------------------
  342. ;  TDATA
  343.  
  344. ;  This is a "temporary" data area that is used to hold the data used
  345. ;  by the transfer routines.
  346.  
  347. TAddr        label    dword
  348. TAddrOff    dw    0        ; the transferr (read to, write from)
  349. TAddrSeg    dw    0        ;  address
  350.  
  351. TDone        dw    0        ; count of sectors done
  352. TCount        dw    0        ; number of sectors to do
  353. TSector        dw    0        ; the sector to be transfer
  354.  
  355. ; ---------------------------------------------------------------------------
  356. ;  The local stack
  357.  
  358.         even            ; make the stack a word-aligned area
  359.         dw    64 dup (0DEADh)
  360. StackTop:
  361.  
  362. ; ---------------------------------------------------------------------------
  363. ;  Strategy Entry Point For the Device Driver
  364.  
  365. ;  This routine simply stores the pointer to the request header
  366. ;  so that request header has it.  That's all it does.  Really.
  367.  
  368. STRATPROC    proc    far
  369.  
  370. Strategy:
  371.     mov    cs:RBPointOff,bx
  372.     mov    cs:RBPointSeg,es    ; just store the pointer
  373.     ret                ; and get outta here!
  374.     ; (isn't it ironic that the shortest routine is called "Strategy"?)
  375. STRATPROC    endp
  376.  
  377. ; ---------------------------------------------------------------------------
  378. ;  Interrupt Entry Point For the Device Driver
  379.  
  380. ;  This routine executes the command contained in the passed request header.
  381. ;  DOS has called STRATEGY, and that routine stored a pointer to the request
  382. ;  header for our use.  We will construct our own stack area because the 
  383. ;  EMM uses a great deal of stack space.
  384.  
  385. INTPROC        proc    far
  386. Interrupt:    
  387. PrintChar 'D'
  388.         mov    CS:SaveSS,ss        ; save the SS register
  389.         mov    CS:SaveSP,sp        ; save the SP register
  390.         mov    CS:SaveAX,ax
  391.  
  392.         cli
  393.         mov    ax,offset StackTop    ; initialize our stack
  394.         mov    sp,ax
  395.         mov    ax,cs
  396.         mov    ss,ax
  397.         sti
  398.  
  399.     ifdef V286
  400.         pusha
  401.     else
  402.         push    bx        ; save the other regs
  403.         push    cx
  404.         push    dx
  405.         push    bp
  406.         push    si
  407.         push    di
  408.     endif
  409.  
  410.         pushf            ; and the flags
  411.         cld            ;  set the string direction up
  412.         push    es
  413.         push    ds
  414.  
  415.         mov    ds,ax        ; setup the data segment register
  416.  
  417.         mov    ax,0FFFFh    ; wipe out any memory of previous
  418.         mov    LastTime,ax    ;  page mappings
  419.  
  420.     ; Note that during calls we use DS to point to our local data
  421.     ; and ES to point to the request header.
  422.  
  423.         les    bx,RBPoint    ; get the request buffer
  424.         mov    al,rq.command    ;  get the command
  425.         cbw
  426.  
  427. ; be sure that the command is in our range
  428.  
  429.         cmp    al,TopCommand    ; fifteen is the highest for us
  430.         jg    badcommand    ;  too high! bad command
  431.  
  432. ; it's a good command - be on the lookout for an unorthodox
  433. ; initialization call
  434.  
  435.         or    al,al        ; is it function zero?
  436.         je    NoSaveM        ;  yes, don't save the map context
  437.  
  438.         push    ax        ;  no, save the AX register
  439.         mov    ah,E_Save    ; save the mapping context
  440.         mov    dx,EMMHandle    ;  under our handle
  441.         int    EMM        ; ask the manager to do it
  442.         or    ah,ah        ; if there was an error,
  443.         jnz    FreakOut    ;  freak out!
  444.         pop    ax        ; if not, get the AX back
  445.  
  446. NoSaveM:    shl    ax,1        ; (one word offset = 2 bytes)
  447.         mov    si,ax
  448.  
  449. ; fake a "short call" by setting the return address to the exit routine
  450.  
  451.         mov    ax,offset BigLog
  452.         push    ax
  453.         xor    ax,ax            ; clear our status
  454.         jmp    cs:JumpTable[si]    ; and hop to it!
  455.         jmp    short BigLog
  456.  
  457. ; ---------------------------------------------------------------------------
  458. ;   We come here if we run into an EMM error.  We'll set the "General
  459. ;   Failure" flag, and return to MS-DOS
  460.  
  461. FreakOut:    mov    rq.status,(errorflag+err_general)
  462.                         ; general failure
  463.                         ; and error settings
  464.         jmp    short BigLog
  465.  
  466. ; ---------------------------------------------------------------------------
  467. ;   Ran into an unsupported command -  set the flag in the status word.
  468.  
  469.  
  470. IOCTLInput:
  471. ReadNoWait:    ; those table entries are invalid commands
  472. InputStatus:
  473. InputFlush:
  474.         pop    ax        ; (forget about the short call)
  475. badcommand:
  476.         mov    ax,(err_unknown+errorflag)  ; an unknown command err
  477.  
  478. ; ---------------------------------------------------------------------------
  479. ;   This is the mass exit; everone splits through this point!  When we
  480. ;   arrive here, the AX reg will contain the word to be put into the
  481. ;   status word.  We'll do that:
  482.  
  483. BigLog:        
  484. PrintChar 'X'
  485.         les    bx,cs:RBPoint        ; point to the request block
  486.         push    ax
  487.         mov    al,rq.command        ; was it an init?
  488.         and    al,al            ;  yes!  don't restore
  489.         pop    ax
  490.         je    NoRestore
  491.  
  492.         push    ax
  493.         mov    ah,E_Restore        ; restore the EMS
  494.         mov    dx,EMMHandle        ;  mapping context
  495.         int    EMM
  496.         or    ah,ah            ; if there was an error
  497.         pop    ax
  498.         jnz    FreakOut
  499.  
  500. NoRestore:    or    ax,donestat        ; set the done status
  501.         mov    rq.status,ax
  502.  
  503. ;  Now, we just undo the registers.
  504.  
  505.         pop    ds        ; the seg regs
  506.         pop    es
  507.  
  508.         popf            ; the flags
  509.     ifdef V286
  510.         popa
  511.     else
  512.         pop    di
  513.         pop    si        ; and the data regs
  514.         pop    bp
  515.         pop    dx
  516.         pop    cx
  517.         pop    bx
  518.     endif
  519.  
  520. PrintChar 'd'
  521.         cli
  522.         mov    ax,CS:SaveAX
  523.         mov    sp,CS:SaveSP    ; restore the calling stack
  524.         mov    ss,CS:SaveSS
  525.         sti
  526.         ret
  527.  
  528. INTPROC        endp
  529.  
  530. ; ---------------------------------------------------------------------------
  531. ;  MEDIA CHECK
  532.  
  533. ;  This command checks to see if the media has been removed and replaced.
  534. ;  Since a RAM drive is non-removable media, this command will always
  535. ;  return a "false".
  536.  
  537. MC        proc    near
  538. PrintChar 'M'
  539. MediaCheck:
  540.         mov    rq.change,1    ; media has not been changed
  541. PrintChar 'm'
  542.         ret            ; return to leave
  543. MC        endp
  544.  
  545. ; ---------------------------------------------------------------------------
  546. ;  BUILD BIOS PARAMETER BLOCK
  547.  
  548. ;  This command simply "builds" a BPB by telling DOS where it is located.
  549.  
  550. BBPB        proc    near
  551. BuildBPB:
  552. PrintChar 'P'
  553.         mov    rq.bpboff,offset OurBPB        ; the offset
  554.         mov    rq.bpbseg,cs            ; in our CS
  555. PrintChar 'p'
  556.         ret
  557.  
  558. BPBArray    dw    offset OurBPB
  559.  
  560. OurBoot:    db    0,0,0
  561.         db    'NiftyEMS'    ;  whodat?
  562.         
  563. OurBPB:
  564. SecSize        dw    512        ; standard DOS sector size
  565. SecPerCluster    db    1        ; sectors per allocation unit
  566.         dw    1        ; number of reserved sectors
  567.         db    1        ; number of copies of the FAT
  568. RDirLen        dw    32        ; number of root directory entries
  569.  
  570. DiskSize    dw    1024        ; number of sectors on the disk
  571.         db    MediaD        ; (media descriptor)
  572. SecPerFAT    dw    1        ; number of sectors per FAT
  573.  
  574.         dw    8        ; sectors per track
  575.         dw    1        ; number of heads
  576.         dw    0        ; number of hidden sectors
  577. BootCode:
  578.  
  579. OurBootLen    equ    this byte - OurBoot
  580.  
  581. BBPB        endp
  582.  
  583.  
  584. ; ---------------------------------------------------------------------------
  585. ;  READ
  586.  
  587. ;  This command reads the specified number of sectors starting at the 
  588. ;  given sector.  It returns the number of sectors actually read.  Errors
  589. ;  are returned if the sector is out of range, or if the number of sectors
  590. ;  is past the end of the disk.  (The error checking is done in the
  591. ;  CLIPPER procedure.)  This procedure doesn't do much itself.  It's
  592. ;  body is mostly a string move instruction.  The starting address
  593. ;  and ending address are set up by the CLIPPER procedure.
  594.  
  595. RSEC        proc    near
  596. Read:
  597.  
  598.     ifdef PCL
  599.         mov    al,'N'
  600.         out    097h,al
  601.         mov    al,'J'
  602.         out    096h,al        ; display "NJ-R" on Smart-Vu
  603.         mov    al,'-'
  604.         out    095h,al
  605.         mov    al,'R'
  606.         out    094h,al
  607.     endif
  608.  
  609. PrintChar 'R'
  610.         call    CLIPPER        ; do the clipping, if need be
  611.  
  612. ReadLoop:    mov    ax,TCount    ; are we done transferring yet?
  613.         cmp    TDone,ax
  614.         je    ReadDone    ; yes!  quit the loop
  615.  
  616.         mov    ax,TSector    ; no ... do some more!
  617.         call    GetSector
  618.         jc    ReadError
  619. PrintChar 'y'
  620.  
  621.         mov    si,di        ; setup the get from address
  622.         les    di,TAddr    ; get the store to address
  623.         mov    cx,256        ; number of words to move
  624.         mov    ds,EMMBase
  625.  
  626.     rep    movsw            ; move it!
  627.  
  628.         mov    ax,cs        ; get addressing back
  629.         mov    ds,ax
  630.  
  631.         add    TAddrOff,512    ; increment transferr address
  632.         inc    TDone
  633.         inc    TSector
  634.         jmp    short ReadLoop
  635.  
  636. ReadDone:    xor    ax,ax        ; clear error flags
  637.  
  638. ReadFinish:    les    bx,[RBPoint]    ; point to request header
  639.         mov    dx,TDone    ; store actual transferred
  640.         mov    rq.Count,dx
  641.         jmp    MakeClick    ; finish clicking
  642.  
  643. ReadError:    mov    ax,(err_badread+errorflag)  ; there was an error!
  644.         jmp    short ReadFinish
  645.  
  646. RSEC        endp
  647.  
  648.  
  649. ; ---------------------------------------------------------------------------
  650. ;  WRITE
  651.  
  652. ;  This command writes the specified number of sectors starting at the
  653. ;  given sector.  It returns the number of sectors actually written.
  654. ;  Errors are returned if the sector is out of range, or if the number
  655. ;  of sectors is past the end of the disk.  (The error checking is done
  656. ;  in the CLIPPER procedure.)  The procedure does very little itself;
  657. ;  it's body consists mostly of a string move instruction.  The
  658. ;  source, destination, and other counts are set up by the CLIPPER
  659. ;  procedure.
  660.  
  661. WSEC        proc    near
  662. Write:
  663.     ifdef PCL
  664.         mov    al,'N'
  665.         out    097h,al
  666.         mov    al,'J'
  667.         out    096h,al        ; display "NJ-W" on Smart-Vu
  668.         mov    al,'-'
  669.         out    095h,al
  670.         mov    al,'W'
  671.         out    094h,al
  672.     endif
  673.  
  674. PrintChar 'W'
  675.         call    CLIPPER        ; do the clipping, if need be
  676.  
  677. WriteLoop:    mov    ax,TCount    ; are we done transferring yet?
  678.         cmp    TDone,ax
  679.         je    WriteDone    ; yep, we are, quit the loop
  680.  
  681.         mov    ax,TSector    ; no ... do some more!
  682.         call    GetSector
  683.         jc    WriteError
  684. PrintChar 'k'
  685.         mov    es,EMMBase
  686.         lds    si,cs:TAddr    ; get the store to address
  687.         mov    cx,256        ; number of words to move
  688.  
  689.     rep    movsw            ; move it!
  690.  
  691.         mov    ax,cs        ; reset addressing back
  692.         mov    ds,ax
  693.  
  694.         add    TAddrOff,512    ; increment transferr address
  695.         inc    TDone        ; count of sectors
  696.         inc    TSector        ; and current sector number
  697.         jmp    short WriteLoop
  698.  
  699. WriteDone:    xor    ax,ax        ; clear error flags
  700.  
  701. WriteFinish:    les    bx,[RBPoint]    ; point to request header
  702.         mov    dx,TDone    ; store actual transferred
  703.         mov    rq.Count,dx
  704.         jmp    MakeClick    ; finish clicking
  705.  
  706. WriteError:    mov    ax,(err_badwrite+errorflag)  ; there was an error!
  707.         jmp    short WriteFinish
  708.  
  709.  
  710. WSEC        endp
  711.  
  712. ; ---------------------------------------------------------------------------
  713. ;  Clipper
  714.  
  715. ;  This local procedure checks the parameters passed to the READ and
  716. ;  WRITE commands to be sure that they are valid.  If they are indeed
  717. ;  valid, it will call the speaker click procedure to take care of
  718. ;  the "audible" options.  It also saves the context of the EMM, and 
  719. ;  sets up the EMM to work with our process.
  720.  
  721. CLIPPER        proc    near
  722.  
  723.         mov    cx,rq.strtsec    ; get the starting sector number
  724.         mov    TSector,cx
  725.         cmp    cx,DiskSize    ; is it larger than the drive?
  726.         jg    RangeError    ;  yes!  there's an error
  727.  
  728.         mov    ax,rq.count
  729.         mov    TCount,ax        ; save it for later
  730.         add    cx,ax        ; add in the number of sec to read
  731.         cmp    cx,DiskSize    ; is it larger than life?
  732.         jle    InRange        ;  no... it's okay
  733.  
  734. RangeError:    pop    ax        ; forget our our return address
  735.         mov    ax,err_badsector; that's a bad sector!
  736.         or    ax,errorflag    ;  (and that's an error in my book)
  737.         mov    rq.count,0    ; no sectors were read, you know
  738.         ret            ;  return back to the dispatcher
  739.  
  740. InRange:    mov    ax,rq.tbaoff        ; get the transfer base addr
  741.         mov    TAddrOff,ax
  742.         mov    ax,rq.tbaseg
  743.         mov    TAddrSeg,ax
  744.         xor    ax,ax        ; zero transferred count
  745.         mov    TDone,ax
  746.  
  747.         ; just flow through to MakeClick
  748.  
  749. CLIPPER        endp
  750.  
  751. ; ---------------------------------------------------------------------------
  752. ;  This is a local procedure that clicks the speaker transparently.  It
  753. ;  is executed at the end of the CLIPPER procedure, which is excuted
  754. ;  at the very beginning of the "READ" and "WRITE" functions.  It is
  755. ;  also JMP'd to at the end of the READ and WRITE routines, and the RET
  756. ;  at the end of this procedure will return to the caller of the READ
  757. ;  and WRITE functions.  (Saves 2 bytes and a bunch of clocks, hey.)
  758.  
  759. SPEAKERCLICK    proc    near
  760.  
  761. MakeClick:    pushf
  762.         cmp    SpeakerFlag,0        ; should we?
  763.         je    NoClick            ;  no, forget it happened
  764.  
  765.         push    ax            ; yes, save the accumulator
  766.         in    al,Speak
  767.         and    al,SpeakMask        ; mask out the bit we don't need
  768.         xor    al,SpeakToggle        ; toggle the control bit
  769.         out    Speak,al        ; and re-output it
  770.         pop    ax            ; retrieve the accumulator
  771.  
  772. NoClick:    popf
  773.         ret            ; return to the caller
  774.  
  775. SpeakerFlag    db    1        ; one if we should be ticking
  776.                     ;    (the default is ticking)
  777.  
  778. SPEAKERCLICK    endp
  779.  
  780. ; ---------------------------------------------------------------------------
  781. ;  GetSector
  782.  
  783. ;  This routine calls the EMM to map the page with the requested sector
  784. ;  into the physical window.  On entry, AX contains the requested sector.
  785. ;  On exit, the EMS is setup so that the requested sector is in the window.
  786. ;  [EMMBASE]:DI will point to it.  This routine is rather funky; it does
  787. ;  the mapping using the slippery shift functions, instead of using DIV
  788. ;  or a lookup table.  *SUPER FAST*!
  789.  
  790. GS        proc    near
  791.  
  792. GetSector:
  793. PrintChar 'G'
  794.         cmp    ax,DiskSize    ; check the range!
  795.         jg    CantGet
  796.  
  797.         push    ax        ; save a copy of the number
  798.     ifdef    V286
  799.         shr    ax,5
  800.     else
  801.         mov    cl,5
  802.         shr    ax,cl        ; divide the sector by 32
  803.                     ;   so that AX=EMM Page
  804.     endif
  805.  
  806.         cmp    ax,LastTime    ; is it the same thing 
  807.         je    Optimized    ;  we got last time?
  808.  
  809.         mov    LastTime,ax    ; remember it for later
  810.  
  811.         mov    bx,ax        ; nah, we'll have to get this one
  812.         mov    ah,E_MapPage
  813.         mov    al,0        ; map it into zero
  814.         mov    dx,EMMHandle
  815.         int    EMM
  816.         or    ah,ah        ; was there an error?
  817.         jne    CantGet
  818.  
  819. Optimized:    pop    ax        ; retrieve the remainder
  820.         and    ax,01Fh        ; mask out high bits of offset
  821.     ifdef V286
  822.         shl    ax,9
  823.     else
  824.         mov    cl,9
  825.         shl    ax,cl        ; find the offset of the sector
  826.     endif
  827.         mov    di,ax
  828.         clc
  829. PrintChar 'g'
  830.         jmp    short MakeClick    ; tick-tock on the way out
  831.  
  832. CantGet:    pop    ax    ; forget the remainder, since there was err
  833.         stc        ; set the error flag, if error
  834.         ret
  835.  
  836. ; An ingenious optimization, if I must say so myself.  This variable holds
  837. ; the last EMS logical page that was fetched by this routine.  This way,
  838. ; the program never gets the same page twice in a row.  It's reset to
  839. ; 0FFFFh by the Interrupt routine so that we won't forget to get a page
  840. ; when one hasn't been attained.  (0FFFFh is a unique code that will never
  841. ; correspond to an actual page.)  Since DOS often does more than one
  842. ; sequential read or write in a single call to the driver, this small
  843. ; feature can save quite a bit of time.
  844.  
  845. LastTime    dw    0FFFFh
  846.  
  847. GS        endp
  848.  
  849. ; ---------------------------------------------------------------------------
  850. ;  This label marks the last byte of the device driver that actually
  851. ;  remains resident.  This driver takes less than 800 bytes, guaranteed.
  852.  
  853. LastResident:
  854. ; ---------------------------------------------------------------------------
  855. ;  INITIALIZE
  856.  
  857. ;  This command sets up the internal data used by NJRAMD.  The procedure
  858. ;  sets the EMM to get the number of pages that the user requests.  (The
  859. ;  information following the specification in the CONFIG.SYS file is
  860. ;  parsed to find the user parameters.  See the NJFRAMD.DOC file to find
  861. ;  the format of the CONFIG information.)  The procedure requests memory
  862. ;  from the EMM
  863.  
  864. Init:
  865. PrintChar 'I'
  866.         mov    dx,offset Banner
  867.         mov    ah,PrintString        ; show our copyright!
  868.         int    21h
  869.  
  870.         mov    ah,GetDOSVersion    ; get the DOS version
  871.         int    21h
  872.         mov    MajorVersion,al
  873.  
  874.         xor    ax,ax            ; point to the 0000 segment
  875.         mov    es,ax
  876.         mov    bx,(EMM*4)+2        ; find the EMM interrupt
  877.         mov    ax,es:[bx]
  878.         mov    es,ax            ; point to the EMM device
  879.         mov    di,10            ; header
  880.  
  881.         mov    si,offset EMMIDString    ; point to the EMM identifier
  882.         mov    cx,8
  883.     repz    cmpsb                ; compare a bunch of bytes
  884.         jz    EMMPresent
  885.  
  886.         mov    dx,offset NoEMMThere    ; point to our error
  887.         jmp    InitFail        ;  the EMM isn't there!!
  888.  
  889. EMMPresent:    ; the Extended Memory Manager is present.  It's okay!
  890.         ; get the EMM Page base, and save it for future reference.
  891.  
  892.         mov    ah,E_PageBase        ; get the page base
  893.         int    EMM
  894.         or    ah,ah
  895.         je    EMMPresent2        ; general failure?
  896. GenFailHook:    jmp    GenFail            ;  (RELATIVE JMP OUT OF RANGE
  897.  
  898. EMMPresent2:    mov    EMMBase,bx        ; save it for later
  899.  
  900.         mov    ah,E_Counts        ; get count of available
  901.         int    EMM            ; memory
  902.         or    ah,ah
  903.         jne    GenFailHook        ; general failure?
  904.                         ;  (RELATIVE JMP OUT OF RANGE)
  905.  
  906.         cmp    bx,0        ; is there any left for me?
  907.         jne    MemForMe
  908.  
  909.         mov    dx,offset NoMem    ; print error
  910.         jmp    InitFail
  911.  
  912. MemForMe:    mov    HowMuch,bx        ; remember how much is left
  913.  
  914.     ; We will now attempt to parse the line of the CONFIG.SYS
  915.     ; file to see if any of our options are on it.
  916.  
  917.         les    bx,[RBPoint]        ; get pointer to header
  918.         les    si,es:[bx+18]        ; get pointer to commands
  919.  
  920. EatingWhite:    mov    al,es:[si]        ; get the next byte
  921.         inc    si
  922.         cmp    al,cr            ; is it a carriage return?
  923.         je    EndOfLine
  924.         cmp    al,'-'            ; is it an option marker?
  925.         je    GotOption        ; yeah! go process it!
  926.         cmp    al,'/'
  927.         jne    EatingWhite        ; no... go back for more
  928.  
  929.     ; We are now pointing at the text of an option.  We will
  930.     ; get the option into the al to see exactly what it is, and we
  931.     ; will then act accordningly.
  932.  
  933. GotOption:    mov    al,es:[si]        ; get the option
  934.         inc    si            ;  and increment the pointer
  935.         cmp    al,'a'            ; bump it to upper case?
  936.         jl    NoBump            ;  no need to
  937.         cmp    al,'z'
  938.         jg    NoBump            ;  no need to
  939.  
  940.         sub    al,('a' - 'A')        ; make it lower case
  941.         
  942. NoBump:        cmp    al,'S'            ; is it a silence option?
  943.         jne    NotSilence        ;   no...
  944.         mov    SpeakerFlag,0        ; yes, it is.  Reset the option!
  945.         jmp    EatingWhite        ; and eat up until end of
  946.                         ;  this option
  947.  
  948. NotSilence:    cmp    al,'P'            ; is it the pages option?
  949.         jne    NotPages
  950.  
  951.     ; We will handle the pages option by reading the command line until
  952.     ; a non-numeric character.  The resulting number will be the number
  953.     ; of pages that the user requested.
  954.  
  955.         xor    dx,dx        ; zero the result
  956.  
  957. PagesLoop:    mov    al,es:[si]    ; get the character
  958.         inc    si
  959.         cmp    al,'0'        ; is it a number?
  960.         jl    LastDigit    ;  nope!
  961.         cmp    al,'9'        ; is it a number?
  962.         jg    LastDigit    ;  note!
  963.  
  964.         push    ax        ; save the digit temporarily
  965.         mov    ax,10
  966.         mul    dx        ; multiply it out
  967.         pop    dx        ; pop the digit into dx
  968.  
  969.         and    dx,0Fh        ; make a decimal digit of it
  970.         add    dx,ax        ; add it into the sum
  971.         jmp    short PagesLoop
  972.  
  973. LastDigit:    mov    RqdPages,dx    ; save requested number of pages
  974.         and    dx,dx        ; is the requested page number zero?
  975.         je    BadPages    ;  yeah!  can't have that
  976.         cmp    al,cr        ; was that last char a CR?
  977.         je    EndOfLine    ;  yes! end of the parse
  978.         jne    EatingWhite    ;  no, go back for more parsing
  979.  
  980. BadPages:    mov    dx,offset TooSmall
  981. BadPages2:    jmp    InitFail
  982.  
  983. NotPages:    cmp    al,'A'            ; is it use all memory?
  984.         jne    NotUseAll
  985.  
  986.         mov    ax,HowMuch
  987.         mov    RqdPages,ax        ; request them all
  988.         jmp    EatingWhite
  989.  
  990. NotUseAll:
  991. Unrecognized:    mov    dx,offset BadOption    ; don't install
  992.         jmp    short BadPages2
  993.  
  994.  
  995.  
  996. EndOfLine:    ; The parsing is done!  We will now check to see if the
  997.         ; requested size is bigger than the available memory.
  998.  
  999.         mov    ax,RqdPages        ; is the reqested amount
  1000.         cmp    HowMuch,ax        ;  greater than available?
  1001.         jge    GoodSize        ; no, size is good
  1002.  
  1003.         mov    dx,offset TooBig    ; yes, that's an error
  1004.         jmp    InitFail
  1005.  
  1006. GoodSize:    ; Now, we'll try to allocate that many pages.  If the user
  1007.         ; didn't specify a number of pages, the default is 32 pages,
  1008.         ; which is 512k of storage.
  1009.  
  1010.         mov    bx,ax
  1011.         mov    ah,E_Open    ; open a new handle of (BX) pages
  1012.         int    EMM
  1013.         or    ah,ah
  1014.         je    GotPages    ; (RELATIVE JMP OUT OF RANGE)
  1015.         jmp    GenFail
  1016.  
  1017. GotPages:    mov    EMMHandle,dx        ; save the handle for later
  1018.  
  1019.     ; We will now setup the information in the BPB to reflect the
  1020.     ; status of the RAM drive.  First, we'll store the DiskSize.
  1021.  
  1022.         mov    ax,RqdPages        ; get number of pages
  1023.     ifdef    V286
  1024.         shl    ax,5
  1025.     else
  1026.         mov    cl,5            ; thirty-two 512-byte sectors
  1027.         shl    ax,cl            ;  in a 16384-byte page
  1028.     endif
  1029.         mov    DiskSize,ax        ; store it in BPB
  1030.  
  1031.     ; Now, we'll figure out how many entries there will be in the
  1032.     ; root directory.  We will allow 1 root directory entry for
  1033.     ; each 2k of storage that the disk has.  We won't allow moer
  1034.     ; than 512 root dir entries, though.
  1035.  
  1036.     ifdef    V286
  1037.         shr    ax,2
  1038.     else
  1039.         shr    ax,1            ; figure out length of
  1040.         shr    ax,1            ; root directory
  1041.     endif
  1042.         cmp    ax,512            ;  1 entry per 2k of storage
  1043.         jl    BigBust            ;  up to 512
  1044.  
  1045.         mov    ax,512
  1046.  
  1047. BigBust:    add    ax,31            ; make sure it's a multiple
  1048.         and    ax,not 31        ; of 32  (round it)
  1049.         mov    RDirLen,ax
  1050.  
  1051.     ; Since we use a 12-bit FAT, we must have 4087 clusters or less.
  1052.     ; We will start with a 1024-byte cluster, and double the cluster
  1053.     ; size until we have enough FAT space.  The maximum amount of
  1054.     ; memory on a single EMS card is 2 megabytes.  A user must
  1055.     ; configure about 3.75 megabytes of memory as a RAM drive to
  1056.     ; cause the program to use 2048-byte clusters... otherwise, the
  1057.     ; drive will have 1024-byte clusters.
  1058.  
  1059.         mov    cx,2            ; Two clusters per sector
  1060.                         ;  for starters.
  1061.  
  1062. ReTry:        mov    ax,DiskSize        ; get the disk size
  1063.         xor    dx,dx
  1064.         div    cx            ; AX = (DiskSize/SPC)
  1065.         cmp    ax,4087            ; is it less than 4087?
  1066.         jl    GoodCombo        ;  yeah!
  1067.         shl    cx,1            ; no. double the SPC and
  1068.         jnc    ReTry            ; try it again
  1069.  
  1070. GoodCombo:    mov    SecPerCluster,cl    ; save SPC number
  1071.  
  1072.     ; AX still is set to the number of clusters on the disk.  Very
  1073.     ; useful number, you know.  We will find now the amount of FAT
  1074.     ; space that is needed.
  1075.  
  1076.         mov    bx,ax        ; ax = clustsers
  1077.         add    ax,ax        ; ax = 2*(clusters)
  1078.         add    ax,bx        ; ax = 3*(clusetrs)
  1079.         shr    ax,1        ; ax = 1.5*(clusters)
  1080.  
  1081.         xor    dx,dx        ;      (FAT Length)
  1082.         mov    cx,512        ; AX =    ----------------
  1083.         div    cx        ;       (BytesPerSector)
  1084.  
  1085.         or    dx,dx        ; is there a remainder?
  1086.         je    NoKludge
  1087.         inc    ax        ;  yes, add another sector
  1088.  
  1089. NoKludge:    mov    SecPerFAT,ax    ; store it in the BPB
  1090.  
  1091.     ; The BPB is now set up properly.  We will now "format" the
  1092.     ; RAM disk.  First, we will have to set all the RAM area to
  1093.     ; zero.  (Even on extremely large "drives", this doesn't take
  1094.     ; very long.  Especially if you have an IBM PS/2 System 80 --
  1095.     ; and then, all the chicks will dig ya!)
  1096.  
  1097.         mov    cx,RqdPages    ; get number of pages in disk
  1098.  
  1099. WipeOut:    mov    bx,cx        ; ask for this page
  1100.         dec    bx
  1101.         mov    dx,EMMHandle    ; into physical page zero
  1102.         mov    ah,E_MapPage
  1103.         mov    al,0
  1104.         int    EMM
  1105.  
  1106.         or    ah,ah
  1107.         je    WipeOut2    ; if there was an error, get out
  1108.         jmp    GenFail        ;    (RELATIVE JMP OUT OF RANGE)
  1109.  
  1110. WipeOut2:    mov    es,EMMBase    ; get addressing to it
  1111.         xor    ax,ax        ; store a zero
  1112.         mov    di,ax        ; zero the destination
  1113.         push    cx
  1114.         mov    cx,8192        ; *words* in a page
  1115.     repne    stosw
  1116.  
  1117.         pop    cx
  1118.         loop    WipeOut        ; if more, go back
  1119.  
  1120.     ; Now that everything is zeroed, we will copy the pseudo-boot
  1121.     ; sector that we have.  DOS uses some of this information while
  1122.     ; reading and writing the disk, so we set it up there.
  1123.  
  1124.         xor    ax,ax        ; get the 0 sector
  1125.         call    GetSector
  1126.         mov    es,EMMBase
  1127.         mov    si,offset OurBoot
  1128.         mov    cx,OurBootLen
  1129.     rep    movsb            ; move it in there
  1130.  
  1131.     ; The boot sector has been written in.  We will now set up
  1132.     ; the FAT.  This task is rather simplified, since we only
  1133.     ; have one copy of the FAT.
  1134.  
  1135.         mov    ax,1
  1136.         call    GetSector    ; get sector 1
  1137.         mov    byte ptr es:[di],MediaD
  1138.         mov    word ptr es:[di+1],0FFFFh
  1139.  
  1140.     ; Now, we will figure out where the first directory sector is.
  1141.     ; *WARNING* - This code assumes that there is only one copy of
  1142.     ; the FAT, and that there is one reserved sector.  If ya change
  1143.     ; the drive to have 2 copies of the FAT, or modify it to have
  1144.     ; reserved sectors (for whatever reason you'd wanna do that),
  1145.     ; you'll have to change this code fragment!
  1146.  
  1147.         mov    ax,SecPerFAT
  1148.         inc    ax        ; AX = first dir sector
  1149.         call    GetSector
  1150.         mov    si,offset OurVolume
  1151.         mov    cx,OurVolumeLen        ; move words
  1152.     rep    movsw
  1153.  
  1154.     ; Phew!  Now the whole thing is done!  We will show the user
  1155.     ; what has been done.  First, we will figure out what device
  1156.     ; tag that we have.  We will tell the user about it.  DOS versions
  1157.     ; earlier than 3.00 don't let us know what our device tag is,
  1158.     ; so we can't tell the user.
  1159.  
  1160.         les    bx,[RBPoint]    ; point to the header, again
  1161.         mov    al,rq.taglet    ; get the tag letter
  1162.         add    al,'A'        ; change it to a capital drive letter.
  1163.         mov    DriveName,al
  1164.  
  1165.         mov    bx,offset LastResident    ; calculate used size
  1166.         xor    ax,ax
  1167.         mov    si,offset UsedSpace
  1168.         call    Bin2Dec            ; store it in the messgae
  1169.  
  1170.         mov    ah,E_Counts        ; find amount of space left
  1171.         int    EMM            ; in the EMS memory
  1172.         or    ah,ah
  1173.         je    FindFree
  1174.         jmp    GenFail
  1175.  
  1176. FindFree:    xor    ax,ax        ; zero high side (the EMM call put
  1177.                     ;  the # of free pages in BX.)
  1178.         mov    cx,14        ; multiply the # of pages by 16k
  1179. CalcEMMFree:    rcl    bx,1        ; shift it low side
  1180.         rcl    ax,1        ;  and carry though to high side
  1181.         loop    CalcEMMFree
  1182.  
  1183.         mov    si,offset Installed2
  1184.         call    Bin2Dec        ; put it into the message!
  1185.  
  1186.         mov    ax,DiskSize    ; get sectors of disk space
  1187.         sub    ax,SecPerFAT    ; subtract space used by the FAT
  1188.         mov    bx,ax        ; put that subtotal in bx
  1189.  
  1190.         mov    ax,RDirLen    ; get the entries in the root dir
  1191.     ifdef V286
  1192.         shr    ax,4
  1193.     else
  1194.         mov    cl,4
  1195.         shr    ax,cl        ; divide by # of entries per sec
  1196.     endif
  1197.         sub    bx,ax        ; subtract some more
  1198.         dec    bx        ; and adjust down
  1199.  
  1200.         mov    cx,9        ; multiply the answer by 512
  1201.         xor    ax,ax        ; zero the high side
  1202. CalcDiskFree:    rcl    bx,1        ; shift low side up
  1203.         rcl    ax,1        ;  shift high side over, with carry
  1204.         loop    CalcDiskFree
  1205.  
  1206.         mov    si,offset Installed
  1207.         call    Bin2Dec        ; store it in the message
  1208.  
  1209.         mov    al,SpeakerFlag        ; is there clicking?
  1210.         or    al,al
  1211.         jne    ClickOkay
  1212.         mov    ah,PrintString
  1213.         mov    dx,offset NoClicking    ; tell the user that it's
  1214.         int    21h            ; been diasbled.
  1215.  
  1216. ClickOkay:    mov    al,MajorVersion
  1217.         cmp    al,3        ; is it version three?
  1218.         je    MsgOkay
  1219.  
  1220.         mov    al,eos        ; its version two
  1221.         mov    DriveName,al
  1222.     
  1223. MsgOkay:    mov    dx,offset Installed    ;print part one of 
  1224.         mov    ah,PrintString        ;  installed! message
  1225.         int    21h
  1226.  
  1227.         mov    dx,offset InstalledB    ;print part two
  1228.         int    21h
  1229.  
  1230.         les    bx,[RBPoint]        ; get that pesky pointer
  1231.  
  1232.         mov    ax,offset LastResident     ; show DOS where we end
  1233.         mov    rq.ndadro,ax        ;   offset
  1234.         mov    ax,cs            ; show DOS were we end
  1235.         mov    rq.ndadrs,ax        ;   segment
  1236.         mov    rq.bpbseg,ax        ; show DOS the BPB array
  1237.  
  1238.         mov    rq.units,1        ; we installed one unit
  1239.         mov    ax,offset BPBArray    ; BPB array offset
  1240.         mov    rq.bpboff,ax
  1241.  
  1242.         xor    ax,ax        ; no return value
  1243.         ret
  1244.  
  1245. ; ---------------------------------------------------------------------------
  1246. ;  Init failure
  1247.  
  1248. ;  We will come here if there is a failure during the initialization
  1249. ;  of the driver.  We print a message letting the user know why we can't
  1250. ;  install, and we then zero ourselves out so that DOS doesn't waste any
  1251. ;  memory on us.
  1252.  
  1253. InitFail:    push    dx        ; save the specific error
  1254.         mov    dx,offset General
  1255.         mov    ah,PrintString
  1256.         int    21h
  1257.  
  1258.         pop    dx        ; now print specific error
  1259.         mov    ah,PrintString
  1260.         int    21h
  1261.  
  1262.         les    bx,[RBPoint]    ; point to the request header
  1263.         mov    ax,cs
  1264.  
  1265.         mov    rq.ndadrs,ax    ; ending address is zero
  1266.  
  1267.         xor    ax,ax        ;  because no memory is taken
  1268.         mov    rq.ndadro,ax    ;  since we failed
  1269.         mov    rq.units,al    ; no units, either
  1270. PrintChar 'i'
  1271.         ret
  1272.  
  1273. ; ---------------------------------------------------------------------------
  1274. ;  General Failure
  1275.  
  1276. ;  There was an EMM Failure during the installation.  If such is the case,
  1277. ;  we will terminate with an error message, and then go to the regular
  1278. ;  fail routine.
  1279.  
  1280. GenFail:    mov    dx,offset EMMError
  1281.         jmp    short InitFail
  1282.  
  1283.  
  1284. ; ---------------------------------------------------------------------------
  1285. ;  Transient Data Area
  1286.  
  1287. ;  The TDA contains the variables used by the Initialization segment of
  1288. ;  the device driver.  It doesn't stay resident.
  1289.  
  1290. HowMuch        dw    ?        ; amount of free EMS, in pages
  1291. RqdPages    dw    32        ; amount of pages requested
  1292.                     ;    (512k is the default)
  1293. MajorVersion    db    3        ; the DOS major version number
  1294.  
  1295. OurVolume    db    'Niftys_Disk'    ; 11-byte volume name
  1296.         db    000001000b    ;  volume label attribute
  1297.         db    10 dup (0)    ; reserved space
  1298.         dw    953
  1299.             ;FEDCBA9876543210b
  1300.             ;YYYYYYYMMMMDDDDD
  1301.         dw    00000111101011111b    ; DATE = Oct 31, 1987
  1302.         db    6 dup (0)    ; more reserved space
  1303.  
  1304. OurVolumeLen    equ    16
  1305.  
  1306. ; ---------------------------------------------------------------------------
  1307. ;  Messages
  1308.  
  1309. ;  These are messages that are used by the initialization section of the
  1310. ;  driver.
  1311.  
  1312. Banner        db    cr,lf,"Nifty James Famous E/EMS RAMdisk Drive",cr,lf
  1313.         db    'Copyright 1987 by Mike Blaszczak',cr,lf
  1314.         db    'Version 1.20 of 16 Oct 1987',cr,lf
  1315.     ifdef V286
  1316.     ifdef PCL
  1317.         db    "PC's Limited Version",cr,lf
  1318.     else
  1319.         db    'Enhanced Processor Version',cr,lf
  1320.     endif
  1321.     endif
  1322.         db    lf,eos
  1323.  
  1324. EMMIDString    db    'EMMXXXX0'
  1325.  
  1326. General        db    'Device not installed.',cr,lf,eos
  1327.  
  1328. NoEMMThere    db    'The EMM is not installed.',cr,lf,lf,eos
  1329.  
  1330. EMMError    db    'EMM failure during installation.',cr,lf,lf,eos
  1331.  
  1332. NoMem        db    'No free EMM Memory.',cr,lf,lf,eos
  1333.  
  1334. TooBig        db    'Requested size too big to fit.',cr,lf,lf,eos
  1335.  
  1336. TooSmall    db    "Can't have zero disk size.",cr,lf,lf,eos
  1337.  
  1338. BadOption    db    'Unrecognized option encountered.',cr,lf,lf,eos
  1339.  
  1340. NoClicking    db    'Clicking suppressed.',cr,lf,eos
  1341.  
  1342. ; "Installed" marks the beginning of the information that is printed
  1343. ; if the device is successfully installed.  The beginning of each
  1344. ; line has eight spaces, which are filled with the information by the
  1345. ; BIN2DEC procedure.  There is then one more space, so that the end
  1346. ; of the number doesn't bump the first word... thus, a total of nine
  1347. ; spaces begin the Installed, Installed2, and UsedSpace labels.
  1348.  
  1349. Installed    db    '         bytes available on RAM drive '
  1350. DriveName    db    '_:.',eos
  1351.  
  1352. InstalledB    db    cr,lf
  1353. Installed2    db    '         bytes left in EMS storage.',cr,lf
  1354. UsedSpace    db    '         bytes of standard DOS memory were'
  1355.         db    ' taken.',cr,lf,lf
  1356.         db    eos
  1357.  
  1358. ; ---------------------------------------------------------------------------
  1359. ;  Init Subroutines
  1360.  
  1361. ;  The following area contains subroutines used by the INIT procedure of
  1362. ;  the device driver.  They aren't kept in memory after the device has been
  1363. ;  installed.
  1364.  
  1365. ; ---------------------------------------------------------------------------
  1366. ;  BIN2DEC
  1367.  
  1368. ;  This routine converts a binary number, in AX:BX, to decimal notation.
  1369. ;  It will convert up to 8 digits, and will supress leading zeros.  The
  1370. ;  routine should be called with DS:SI set to point to the area to store
  1371. ;  the converted number.
  1372.  
  1373. Bin2Dec        proc    near
  1374.  
  1375.         push    es        ; save the registers
  1376.         push    ds
  1377.         push    di
  1378.         push    si
  1379.  
  1380.         mov    WorkAreaL,bx
  1381.         mov    WorkAreaH,ax    ; put the number on our scratchpad
  1382.  
  1383.         mov    ax,ds        ; point to the answer with ES:DI
  1384.         mov    es,ax
  1385.         mov    di,si
  1386.         add    di,7
  1387.  
  1388.         mov    si,offset WorkAreaL    ; point at scratchpad
  1389.  
  1390. Bin2DecLoop:    push    si
  1391.  
  1392.         xor    bx,bx        ; done flag
  1393.         mov    cx,2        ; 2 words in our number
  1394.         mov    dx,bx        ; clear remainder
  1395.         add    si,2        ; point to the high end
  1396.  
  1397. Bin2DecDigit:    push    cx        ; save word count
  1398.         mov    ax,[si]        ; get the digit
  1399.         mov    cx,10
  1400.         div    cx        ; convert it
  1401.         mov    [si],ax        ; store it back
  1402.         or    bx,ax        ; set the done flag appropriately
  1403.         sub    si,2        ; point to next lower
  1404.         pop    cx
  1405.         loop    Bin2DecDigit
  1406.  
  1407.         or    dl,'0'        ; make it into a decimal digit
  1408.         mov    [di],dl        ; and store it
  1409.         dec    di        ; adjust pointer
  1410.  
  1411.         pop    si        ; get the pointer back
  1412.         and    bx,bx        ; is the result zero?
  1413.         jne    Bin2DecLoop    ; nope!  Do more!
  1414.  
  1415.         pop    si        ; retrieve the used registers
  1416.         pop    di
  1417.         pop    ds
  1418.         pop    es
  1419.         ret
  1420.  
  1421. WorkAreaL    dw    0        ; low end of the work area
  1422. WorkAreaH    dw    0        ; high side of the work area
  1423.  
  1424. Bin2Dec        endp
  1425.  
  1426. driver        ends
  1427.         end
  1428.  
  1429. ; That's a wrap.
  1430. ; Special thanks to Bob Brody and Dr File Finder~~~~~~~~~~~~.
  1431.  
  1432.